Quantum Circuit Model

Set of wires (quantum register) on which a sequence of quantum gates is applied.

A quantum circuit represents our computation.

Each gate is a transformation.

Some properties:

  1. times flow left to right;
  2. each string is a qubit;
  3. circuit depth = maximum number of gates acting on a qubit.
In [4]:
%matplotlib inline
from qiskit.circuit.random import random_circuit
random_circuit(4, 4).draw(output='mpl')
Out[4]:

IBM Q Experience

This platform lets you access some quantum processors for free, through the cloud. The user interacts with IBM machines using quantum circuits through the Python library Qiskit.

Documentation

Can be found on Qiskit Learn.

Main resource: Qiskit textbook

Some interesting videos on the Youtube channel.

Other resources

Robert Sutor

Some practical things you can do to learn about quantum computing

The quantum game

Quantum Intuition (Youtube) (website)

Hello world!

In [1]:
%matplotlib inline
from qiskit import *
from math import pi
qiskit.__qiskit_version__
Out[1]:
{'qiskit-terra': '0.15.2',
 'qiskit-aer': '0.6.1',
 'qiskit-ignis': '0.4.0',
 'qiskit-ibmq-provider': '0.8.0',
 'qiskit-aqua': '0.7.5',
 'qiskit': '0.20.1'}
In [6]:
qreg = QuantumRegister(2, "qr")
In [7]:
creg = ClassicalRegister(2, "cr")
In [8]:
circuit = QuantumCircuit(qreg, creg)
In [9]:
circuit.draw()
Out[9]:
      
qr_0: 
      
qr_1: 
      
cr: 2/
      
In [10]:
circuit.h(qreg[0])
circuit.draw(output='mpl')
Out[10]:
In [11]:
circuit.cx(qreg[0], qreg[1])
circuit.draw(output='mpl')
Out[11]:
In [12]:
circuit.measure(qreg, creg)
circuit.draw(output='mpl')
Out[12]:
In [13]:
simulator = Aer.get_backend('qasm_simulator')
In [14]:
result = execute(circuit, backend=simulator, shots=1024).result()
In [15]:
from qiskit.tools.visualization import plot_histogram
In [16]:
plot_histogram(result.get_counts())
Out[16]:

Hello quantum world!

title

In [14]:
IBMQ.save_account("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                  overwrite=True)
# 401 Client Error: Unauthorized for url: https://auth.quantum-computing.ibm.com/api/users/loginWithToken
In [17]:
IBMQ.load_account()
/home/incud/anaconda3/lib/python3.7/site-packages/qiskit/providers/ibmq/ibmqfactory.py:192: UserWarning: Timestamps in IBMQ backend properties, jobs, and job results are all now in local time instead of UTC.
  warnings.warn('Timestamps in IBMQ backend properties, jobs, and job results '
Out[17]:
<AccountProvider for IBMQ(hub='ibm-q', group='open', project='main')>
In [18]:
provider = IBMQ.get_provider('ibm-q')
qcomp = provider.get_backend('ibmq_5_yorktown')
In [17]:
job = execute(circuit, backend=qcomp, shots=1024)
In [18]:
from qiskit.tools.monitor import job_monitor
job_monitor(job)
Job Status: job has successfully run
In [19]:
result_2 = job.result()
plot_histogram([result.get_counts(), result_2.get_counts()])
Out[19]:

Unitary transformations

Matrix notation

$$ X = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix}$$

Unitary transformation from circuit

In [20]:
circuit = QuantumCircuit(1)
circuit.x(0)
unitary = execute(circuit, backend=Aer.get_backend('unitary_simulator')).result().get_unitary()
unitary
Out[20]:
array([[0.+0.j, 1.+0.j],
       [1.+0.j, 0.+0.j]])

Bloch sphere notation

In [2]:
import kaleidoscope.qiskit
from kaleidoscope import qsphere, bloch_sphere
from qiskit.quantum_info import Statevector
/home/incud/.local/lib/python3.7/site-packages/numba/np/ufunc/parallel.py:363: NumbaWarning: The TBB threading layer requires TBB version 2019.5 or later i.e., TBB_INTERFACE_VERSION >= 11005. Found TBB_INTERFACE_VERSION = 11004. The TBB threading layer is disabled.
  warnings.warn(problem)
In [3]:
circuit = QuantumCircuit(1)
circuit.x(0)
# statevector = execute(circuit, backend=Aer.get_backend('statevector_simulator')).result().get_statevector()
statevector = Statevector.from_instruction(circuit)
qsphere(statevector)

Single qubit gates

Identity gate (no gates)

In [4]:
circuit = QuantumCircuit(1)
statevector = execute(circuit, backend=Aer.get_backend('statevector_simulator')).result().get_statevector()
qsphere(statevector)
/home/incud/anaconda3/lib/python3.7/site-packages/qiskit/providers/ibmq/ibmqfactory.py:192: UserWarning:

Timestamps in IBMQ backend properties, jobs, and job results are all now in local time instead of UTC.

X

$$ X |0\rangle = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix} \begin{bmatrix} 1 \\ 0 \end{bmatrix} = \begin{bmatrix} 0 \\ 1 \end{bmatrix} = |1\rangle $$
In [5]:
circuit = QuantumCircuit(1)
circuit.x(0)
qsphere(circuit.statevector())

RX

$$ R_x(\theta) |0\rangle = \begin{bmatrix} \cos(\theta/2) & -i\sin(\theta/2) \\ -i\sin(\theta/2) & \cos(\theta/2) \end{bmatrix} \begin{bmatrix} 1 \\ 0 \end{bmatrix} = \begin{bmatrix} \cos(\theta/2) \\ - i \sin(\theta/2) \end{bmatrix} $$$$ R_x(\pi/4) |0\rangle = \begin{bmatrix} \cos(\pi/8) \\ - i \sin(\pi/8) \end{bmatrix} = \begin{bmatrix} 0.9239 \\ - i 0.3826 \end{bmatrix} = 0.9239 |0\rangle - i 0.3826 |1\rangle $$$$(0.9239)^2 + (0.3826)^2 \approx 1$$
In [6]:
circuit = QuantumCircuit(1)
circuit.rx(pi/4, 0)
bloch_sphere(circuit.statevector())
In [7]:
qsphere(circuit.statevector())
Colore delle fasi
  • $z = r e^{i \theta}$
  • $1 = e^{i 0} \to \theta = 0 \to$ rosso
  • $i = e^{i pi/2} \to \theta = \pi/2 \to$ verde
  • $-1 = e^{i \pi} \to \theta = \pi \to$ azzurro
  • $-i = e^{i 3pi/2} \to \theta = 3\pi/2 \to$ viola

Partendo da $\theta$:

  • $\theta = \pi/4 \to e^{i \pi/4} = \sqrt{2} + i \sqrt{2}$

Applico Y

$$ Y |0\rangle = \begin{bmatrix} 0 & -i \\ i & 0 \end{bmatrix} \begin{bmatrix} 1 \\ 0 \end{bmatrix} = \begin{bmatrix} 0 \\ i \end{bmatrix} = i |1\rangle $$
In [8]:
circuit = QuantumCircuit(1)
circuit.y(0)
qsphere(Statevector.from_instruction(circuit))

Z

$$ Z |0\rangle = \begin{bmatrix} 1 & 0 \\ 0 & -1 \end{bmatrix} \begin{bmatrix} 1 \\ 0 \end{bmatrix} = \begin{bmatrix} 1 \\ 0 \end{bmatrix} = |0\rangle $$$$ Z |1\rangle = \begin{bmatrix} 1 & 0 \\ 0 & -1 \end{bmatrix} \begin{bmatrix} 0 \\ 1 \end{bmatrix} = \begin{bmatrix} 1 \\ 0 \end{bmatrix} = -|1\rangle $$
In [9]:
circuit = QuantumCircuit(1)
circuit.x(0)
circuit.z(0)
qsphere(circuit.statevector())

Applico H

$$ H |0\rangle = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix} \begin{bmatrix} 1 \\ 0 \end{bmatrix} = |+\rangle $$
In [10]:
circuit = QuantumCircuit(1)
circuit.h(0)
qsphere(circuit.statevector())

Transpiler

Source-to-source compiler, its input is the circuit, and the name of a specific architecture. Its output is an equivalent circuit that can run on that specific architecture, optimized.

In [30]:
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.crx(0.12, 0,1)
circuit.draw(output="mpl")
Out[30]:
In [31]:
provider = IBMQ.get_provider('ibm-q')
backend = provider.get_backend('ibmq_5_yorktown')
new_circuit = transpile(circuit, backend)
new_circuit.draw()
Out[31]:
               ┌──────────┐                                               
      q_0 -> 0 ┤ U2(0,pi) ├──■─────────────────────■──────────────────────
               ├──────────┤┌─┴─┐┌───────────────┐┌─┴─┐┌──────────────────┐
      q_1 -> 1 ┤ U1(pi/2) ├┤ X ├┤ U3(-0.06,0,0) ├┤ X ├┤ U3(0.06,-pi/2,0) ├
               └──────────┘└───┘└───────────────┘└───┘└──────────────────┘
ancilla_0 -> 2 ───────────────────────────────────────────────────────────
                                                                          
ancilla_1 -> 3 ───────────────────────────────────────────────────────────
                                                                          
ancilla_2 -> 4 ───────────────────────────────────────────────────────────
                                                                          

Initialization

A single qubit can be initialized with a $u3$ gate:

$$ u3(\theta, \phi, \lambda) = \begin{bmatrix} \cos(\theta/2) & - e^{i \lambda} \sin(\theta/2) \\ e^{i \phi} \sin(\theta/2) & e^{i \phi + i \lambda} cos(\theta/2) \end{bmatrix} $$$$ u3(\frac{\pi}{2}, \frac{\pi}{2}, \frac{\pi}{2}) = = \begin{bmatrix} \cos(\pi/4) & - e^{i \pi/2} \sin(\pi/4) \\ e^{i \pi/2} \sin(\pi/4) & e^{i \pi} \cos(\pi/4) \end{bmatrix} = \begin{bmatrix} .707 & - i .707 \\ i .707 & - .707 \end{bmatrix}$$
In [32]:
qc = QuantumCircuit(1)
qc.u3(pi/2, pi/2, pi/2, 0)
qc.draw()
Out[32]:
     ┌────────────────────┐
q_0: ┤ U3(pi/2,pi/2,pi/2) ├
     └────────────────────┘
In [33]:
job = execute(qc, Aer.get_backend('unitary_simulator'))
array = job.result().get_unitary(qc)
# rounding result
for i in range(0,2):
    for j in range(0,2):
        a = round(array[i][j].real, 3)
        b = round(array[i][j].imag, 3)
        array[i][j] = complex(a, b)
array
Out[33]:
array([[ 0.707+0.j   , -0.   -0.707j],
       [ 0.   +0.707j, -0.707+0.j   ]])

Arbitrary initialization

Based on Shende's algorithm (see details here).

In [34]:
import numpy as np
import math
desired_vector = [
    1 / math.sqrt(16) * complex(0, 1),
    1 / math.sqrt(8) * complex(1, 0),
    1 / math.sqrt(16) * complex(1, 1),
    0,
    0,
    1 / math.sqrt(8) * complex(1, 2),
    1 / math.sqrt(16) * complex(1, 0),
    0]
print("Amplitude: ", np.linalg.norm(desired_vector))

circuit = QuantumCircuit(3)
circuit.initialize(desired_vector, [0, 1, 2] )
circuit.draw()
Amplitude:  0.9999999999999999
Out[34]:
     ┌───────────────────────────────────────────────────────────────────┐
q_0: ┤0                                                                  ├
     │                                                                   │
q_1: ┤1 initialize(0.25j,0.35355,0.25+0.25j,0,0,0.35355+0.70711j,0.25,0) ├
     │                                                                   │
q_2: ┤2                                                                  ├
     └───────────────────────────────────────────────────────────────────┘
In [35]:
provider = IBMQ.get_provider('ibm-q')
backend = provider.get_backend('ibmq_5_yorktown')
new_circuit = transpile(circuit, backend)
new_circuit.draw()
Out[35]:
                   ┌────────────────┐                                       »
      q_0 -> 0 ────┤ U3(1.2631,0,0) ├───────────────────────────────────────»
                   ├───────────────┬┘   ┌───┐┌────────────────────────┐┌───┐»
      q_1 -> 1 ────┤ U3(0.991,0,0) ├────┤ X ├┤ U3(0.37844,0.080438,0) ├┤ X ├»
               ┌───┴───────────────┴───┐└─┬─┘└────────────────────────┘└─┬─┘»
      q_2 -> 2 ┤ U3(1.9552,-0.31226,0) ├──■──────────────────────────────■──»
               └───────────────────────┘                                    »
ancilla_0 -> 3 ─────────────────────────────────────────────────────────────»
                                                                            »
ancilla_1 -> 4 ─────────────────────────────────────────────────────────────»
                                                                            »
«                               ┌───┐┌────────────────┐┌───┐»
«      q_0 -> 0 ────────────────┤ X ├┤ U3(1.2631,0,0) ├┤ X ├»
«               ┌──────────────┐└─┬─┘└────────────────┘└─┬─┘»
«      q_1 -> 1 ┤ U1(-0.47314) ├──■──────────────────────┼──»
«               └──────────────┘                         │  »
«      q_2 -> 2 ─────────────────────────────────────────■──»
«                                                           »
«ancilla_0 -> 3 ────────────────────────────────────────────»
«                                                           »
«ancilla_1 -> 4 ────────────────────────────────────────────»
«                                                           »
«               ┌──────────────────┐┌───┐┌─────────────────────────┐┌───┐»
«      q_0 -> 0 ┤ U3(-0.30774,0,0) ├┤ X ├┤ U3(-0.30774,-0.86584,0) ├┤ X ├»
«               └──────────────────┘└─┬─┘└─────────────────────────┘└─┬─┘»
«      q_1 -> 1 ──────────────────────■───────────────────────────────■──»
«                                                                        »
«      q_2 -> 2 ─────────────────────────────────────────────────────────»
«                                                                        »
«ancilla_0 -> 3 ─────────────────────────────────────────────────────────»
«                                                                        »
«ancilla_1 -> 4 ─────────────────────────────────────────────────────────»
«                                                                        »
«               ┌──────────────┐┌───┐┌──────────────┐┌───┐┌──────────────┐
«      q_0 -> 0 ┤ U1(-0.47314) ├┤ X ├┤ U1(0.080438) ├┤ X ├┤ U1(-0.31226) ├
«               └──────────────┘└─┬─┘└──────────────┘└─┬─┘└──────────────┘
«      q_1 -> 1 ──────────────────┼────────────────────■──────────────────
«                                 │                                       
«      q_2 -> 2 ──────────────────■───────────────────────────────────────
«                                                                         
«ancilla_0 -> 3 ──────────────────────────────────────────────────────────
«                                                                         
«ancilla_1 -> 4 ──────────────────────────────────────────────────────────
«                                                                         
In [36]:
new_circuit.size()
Out[36]:
19

Try with a random generated state...

In [37]:
from qiskit.quantum_info.states.random import random_statevector

def check_circuit_size(dim, qubits, machine_name='ibmq_5_yorktown'):
    wanted_state = random_statevector(dim).data
    circuit = QuantumCircuit(len(qubits))
    circuit.initialize(wanted_state, qubits )
    circuit.draw()
    provider = IBMQ.get_provider('ibm-q')
    backend = provider.get_backend(machine_name)
    new_circuit = transpile(circuit, backend)
    size = new_circuit.size()
    print("{}: Filling {} qubits => {} elements => {} gates".format(machine_name, len(qubits), dim, size))
    
check_circuit_size( 8, [0, 1, 2])
check_circuit_size(16, [0, 1, 2, 3])
check_circuit_size(32, [0, 1, 2, 3, 4])

check_circuit_size( 8, [0, 1, 2], machine_name='ibmq_16_melbourne')
check_circuit_size(16, [0, 1, 2, 3], machine_name='ibmq_16_melbourne')
check_circuit_size(32, [0, 1, 2, 3, 4], machine_name='ibmq_16_melbourne')
check_circuit_size(64, [0, 1, 2, 3, 4, 5], machine_name='ibmq_16_melbourne')
check_circuit_size(128, [0, 1, 2, 3, 4, 5, 6], machine_name='ibmq_16_melbourne')
check_circuit_size(256, [0, 1, 2, 3, 4, 5, 6, 7], machine_name='ibmq_16_melbourne')
ibmq_5_yorktown: Filling 3 qubits => 8 elements => 19 gates
ibmq_5_yorktown: Filling 4 qubits => 16 elements => 72 gates
ibmq_5_yorktown: Filling 5 qubits => 32 elements => 121 gates
ibmq_16_melbourne: Filling 3 qubits => 8 elements => 22 gates
ibmq_16_melbourne: Filling 4 qubits => 16 elements => 83 gates
ibmq_16_melbourne: Filling 5 qubits => 32 elements => 210 gates
ibmq_16_melbourne: Filling 6 qubits => 64 elements => 459 gates
ibmq_16_melbourne: Filling 7 qubits => 128 elements => 1029 gates
ibmq_16_melbourne: Filling 8 qubits => 256 elements => 1863 gates

Exercises

Using the NOT gate (expressed as x in Qiskit), the CNOT gate (expressed as cx in Qiskit) and the Toffoli gate (expressed as ccx in Qiskit) create functions to implement the XOR, AND, NAND and OR gates.

Hint

In [ ]: